#include "ThreadCache.h"
#include "CentralCache.h"

void* ThreadCache::Allocate(size_t size)
{
	assert(size < MAX_BYTES);
	assert(size > 0);
	size_t alignSize = SizeClass::RoundUp(size);
	size_t index = SizeClass::Index(size);

	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].pop();
	}
	else
	{
		return FetchFromCentralCache(index, alignSize);
	}
}

void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	assert(index <= NFREELIST);
	size_t& FreeListMaxSize = _freeLists[index].MaxSize();
	size_t batchNum = min(SizeClass::NumMoveSize(size), FreeListMaxSize);

	if (batchNum == FreeListMaxSize) FreeListMaxSize += 1;
	batchNum = FreeListMaxSize;
	void* start = nullptr, * end = nullptr;
	size_t n = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);

	if (n == 1) return start;
	_freeLists[index].push_range(NextObj(start), end, n - 1);
	return start;
}

void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);

	size_t index = SizeClass::Index(size);
	_freeLists[index].push(ptr);

	if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}
}

void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr, * end = nullptr; 
	list.pop_range(start, end, list.MaxSize());

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

